/*
 * Copyright (C) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

extern crate alloc;

use core::convert::TryFrom;
use crate::vec;

use crate::commands::common_command::*;
use crate::common::types::Udid;
use crate::common::{AuthType, ErrorCode, ExecutorRole, ScheduleMode};
use crate::entry::user_auth_ffi::*;
use crate::impl_command_handler;
use crate::traits::command_manager::{AccessPolicy, CommandHandler};
use crate::traits::misc_manager::MiscManagerRegistry;
use crate::traits::executor_manager::{ExecutorManagerRegistry, ExecutorInfo};
use crate::utils::attribute::{Attribute, AttributeKey};
use crate::utils::message_codec::{MessageCodec, MessageSignParam};
use crate::{log_e, log_i, Box, Vec, TemplateId};

// PrepareRemoteAuth - implementation
pub type PrepareRemoteAuthInput = PrepareRemoteAuthInputFfi;
pub type PrepareRemoteAuthOutput = PrepareRemoteAuthOutputFfi;

pub trait PrepareRemoteAuthCommandExecutor {
    fn execute(&self, input: PrepareRemoteAuthInput) -> Result<PrepareRemoteAuthOutput, ErrorCode>;
}

pub struct DefaultPrepareRemoteAuthCommandExecutor;

impl PrepareRemoteAuthCommandExecutor for DefaultPrepareRemoteAuthCommandExecutor {
    fn execute(
        &self,
        _input: PrepareRemoteAuthInput,
    ) -> Result<PrepareRemoteAuthOutput, ErrorCode> {
        log_i!("prepare remote auth is not implemented");
        Ok(PrepareRemoteAuthOutput::default())
    }
}

impl_command_handler!(
    PrepareRemoteAuthCommandHandler,
    PrepareRemoteAuthInputFfi,
    PrepareRemoteAuthInput,
    PrepareRemoteAuthOutputFfi,
    Box<dyn PrepareRemoteAuthCommandExecutor>
);

// GetLocalScheduleFromMessage - implementation
pub struct GetLocalScheduleFromMessageInput {
    pub remote_udid: Udid,
    pub message: Vec<u8>,
}

impl TryFrom<GetLocalScheduleFromMessageInputFfi> for GetLocalScheduleFromMessageInput {
    type Error = ErrorCode;
    fn try_from(ffi: GetLocalScheduleFromMessageInputFfi) -> Result<Self, ErrorCode> {
        Ok(GetLocalScheduleFromMessageInput {
            remote_udid: Udid(ffi.remote_udid),
            message: ffi.message.try_into()?,
        })
    }
}

#[derive(Clone, Default)]
pub struct GetLocalScheduleFromMessageOutput {
    pub schedule_info: ScheduleInfo,
}

impl TryFrom<GetLocalScheduleFromMessageOutput> for GetLocalScheduleFromMessageOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetLocalScheduleFromMessageOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(GetLocalScheduleFromMessageOutputFfi {
            schedule_info: value.schedule_info.try_into()?,
        })
    }
}

pub trait GetLocalScheduleFromMessageCommandExecutor {
    fn execute(
        &self,
        input: GetLocalScheduleFromMessageInput,
    ) -> Result<GetLocalScheduleFromMessageOutput, ErrorCode>;
}

pub struct DefaultGetLocalScheduleFromMessageCommandExecutor;

impl GetLocalScheduleFromMessageCommandExecutor
    for DefaultGetLocalScheduleFromMessageCommandExecutor
{
    fn execute(
        &self,
        input: GetLocalScheduleFromMessageInput,
    ) -> Result<GetLocalScheduleFromMessageOutput, ErrorCode> {       
        let codec = MessageCodec::new(MessageSignParam::CrossDevice(input.remote_udid));
        let attr = codec.deserialize_attribute(input.message.as_slice())?;
        
        let schedule_id = attr.get_u64(AttributeKey::AttrScheduleId)?;
        let auth_type = AuthType::try_from(attr.get_i32(AttributeKey::AttrType)?)?;
        let executor_matcher = attr.get_u32(AttributeKey::AttrExecutorMatcher)?;
        let schedule_mode = ScheduleMode::try_from(attr.get_i32(AttributeKey::AttrScheduleMode)?)?;
        let executor_role = ExecutorRole::try_from(attr.get_i32(AttributeKey::AttrExecutorRole)?)?;
        let verifier_udid = Udid::try_from(attr.get_u8_vec(AttributeKey::AttrVerifierUdid)?)?;
        let collector_udid = Udid::try_from(attr.get_u8_vec(AttributeKey::AttrCollectorUdid)?)?;

        if collector_udid != MiscManagerRegistry::get().get_udid()? {
            log_e!("Collector udid mismatch");
            return Err(ErrorCode::BadParam);
        }

        if verifier_udid == MiscManagerRegistry::get().get_udid()? {
            log_e!("Verifier udid is local udid");
            return Err(ErrorCode::BadParam);
        }
        
        let filter = Box::new(move |executor: &ExecutorInfo| {
            executor.auth_type == auth_type && executor.matcher == executor_matcher && executor.role == executor_role && executor.device_udid == collector_udid
        });
        let executor = ExecutorManagerRegistry::get().get_executor(filter)?;
        
        let mut schedule_info = ScheduleInfo::default();
        schedule_info.schedule_id = schedule_id;
        schedule_info.auth_type = auth_type;
        schedule_info.executor_matcher = executor_matcher;
        schedule_info.schedule_mode = schedule_mode;
        schedule_info.executor_indexes = vec![executor.executor_index];

        let public_key = attr.get_u8_slice(AttributeKey::AttrPublicKey)?;
        let challenge = attr.get_u8_slice(AttributeKey::AttrChallenge)?;

        let mut attribute = Attribute::new();
        attribute.set_u64(AttributeKey::AttrScheduleId, schedule_id);
        attribute.set_i32(AttributeKey::AttrType, auth_type as i32);
        attribute.set_u32(AttributeKey::AttrExecutorMatcher, executor_matcher);
        attribute.set_i32(AttributeKey::AttrScheduleMode, schedule_mode as i32);
        attribute.set_u64(AttributeKey::AttrExecutorIndex, executor.executor_index);
        attribute.set_u8_slice(AttributeKey::AttrPublicKey, public_key);
        attribute.set_u8_slice(AttributeKey::AttrChallenge, challenge);

        let key_pair = MiscManagerRegistry::get().get_local_key_pair()?.clone();
        let codec = MessageCodec::new(MessageSignParam::Executor(key_pair));
        let signed_data = codec.serialize_attribute(&attribute)?;
        schedule_info.executor_messages = vec![signed_data];

        Ok(GetLocalScheduleFromMessageOutput { schedule_info })
    }
}

impl_command_handler!(
    GetLocalScheduleFromMessageCommandHandler,
    GetLocalScheduleFromMessageInputFfi,
    GetLocalScheduleFromMessageInput,
    GetLocalScheduleFromMessageOutputFfi,
    Box<dyn GetLocalScheduleFromMessageCommandExecutor>
);

// GetAuthResultFromMessage - implementation
pub struct GetAuthResultFromMessageInput {
    pub remote_udid: Udid,
    pub message: Vec<u8>,
}

impl TryFrom<GetAuthResultFromMessageInputFfi> for GetAuthResultFromMessageInput {
    type Error = ErrorCode;

    fn try_from(ffi: GetAuthResultFromMessageInputFfi) -> Result<Self, ErrorCode> {
        Ok(GetAuthResultFromMessageInput {
            remote_udid: Udid(ffi.remote_udid),
            message: ffi.message.try_into()?,
        })
    }
}

#[derive(Clone, Default)]
pub struct GetAuthResultFromMessageOutput {
    pub auth_result_info: AuthResultInfo,
}

impl TryFrom<GetAuthResultFromMessageOutput> for GetAuthResultFromMessageOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetAuthResultFromMessageOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(GetAuthResultFromMessageOutputFfi {
            auth_result_info: value.auth_result_info.try_into()?,
        })
    }
}

pub trait GetAuthResultFromMessageCommandExecutor {
    fn execute(
        &self,
        input: GetAuthResultFromMessageInput,
    ) -> Result<GetAuthResultFromMessageOutput, ErrorCode>;
}

pub struct DefaultGetAuthResultFromMessageCommandExecutor;

impl GetAuthResultFromMessageCommandExecutor for DefaultGetAuthResultFromMessageCommandExecutor {
    fn execute(
        &self,
        input: GetAuthResultFromMessageInput,
    ) -> Result<GetAuthResultFromMessageOutput, ErrorCode> {
        let codec = MessageCodec::new(MessageSignParam::CrossDevice(input.remote_udid));
        let attr = codec.deserialize_attribute(input.message.as_slice())?;

        let mut auth_result_info = AuthResultInfo::default();
        auth_result_info.result = attr.get_i32(AttributeKey::AttrResult)?;
        auth_result_info.lockout_duration = attr.get_i32(AttributeKey::AttrLockoutDuration)?;
        auth_result_info.remain_attempts = attr.get_i32(AttributeKey::AttrRemainAttempts)?;
        auth_result_info.user_id = attr.get_i32(AttributeKey::AttrUserId)?;
        auth_result_info.token = attr.get_u8_vec(AttributeKey::AttrToken)?.clone();

        Ok(GetAuthResultFromMessageOutput { auth_result_info })
    }
}

impl_command_handler!(
    GetAuthResultFromMessageCommandHandler,
    GetAuthResultFromMessageInputFfi,
    GetAuthResultFromMessageInput,
    GetAuthResultFromMessageOutputFfi,
    Box<dyn GetAuthResultFromMessageCommandExecutor>
);

// GetSignedExecutorInfo - implementation
#[derive(Clone)]
pub struct GetSignedExecutorInfoInput {
    pub auth_types: Vec<AuthType>,
    pub executor_role: ExecutorRole,
    pub remote_udid: Udid,
}

impl TryFrom<GetSignedExecutorInfoInputFfi> for GetSignedExecutorInfoInput {
    type Error = ErrorCode;

    fn try_from(ffi: GetSignedExecutorInfoInputFfi) -> Result<Self, ErrorCode> {
        Ok(GetSignedExecutorInfoInput {
            auth_types: Vec::try_from(ffi.auth_types)?,
            executor_role: ExecutorRole::try_from(ffi.executor_role)?,
            remote_udid: Udid(ffi.remote_udid),
        })
    }
}

#[derive(Clone, Default)]
pub struct GetSignedExecutorInfoOutput {
    pub signed_executor_info: Vec<u8>,
}

impl TryFrom<GetSignedExecutorInfoOutput> for GetSignedExecutorInfoOutputFfi {
    type Error = ErrorCode;

    fn try_from(value: GetSignedExecutorInfoOutput) -> core::result::Result<Self, ErrorCode> {
        Ok(GetSignedExecutorInfoOutputFfi {
            signed_executor_info: value.signed_executor_info.try_into()?,
        })
    }
}

pub trait GetSignedExecutorInfoCommandExecutor {
    fn execute(
        &self,
        input: GetSignedExecutorInfoInput,
    ) -> Result<GetSignedExecutorInfoOutput, ErrorCode>;
}

pub struct DefaultGetSignedExecutorInfoCommandExecutor;

impl GetSignedExecutorInfoCommandExecutor for DefaultGetSignedExecutorInfoCommandExecutor {
    fn execute(
        &self,
        input: GetSignedExecutorInfoInput,
    ) -> Result<GetSignedExecutorInfoOutput, ErrorCode> {
        let input_clone = input.clone();
        let filter = Box::new(move |executor: &ExecutorInfo| {
            input_clone.auth_types.contains(&executor.auth_type) && executor.role == input_clone.executor_role && executor.device_udid == input_clone.remote_udid
        });
        let executors = ExecutorManagerRegistry::get().get_executor_list(filter);

        let mut sub_messages = Vec::new();
        for executor in &executors {
            let mut executor_attr = Attribute::new();
            executor_attr.set_u64(AttributeKey::AttrExecutorIndex, executor.executor_index);
            executor_attr.set_i32(AttributeKey::AttrType, executor.auth_type as i32);
            executor_attr.set_i32(AttributeKey::AttrExecutorRole, executor.role as i32);
            executor_attr.set_i32(AttributeKey::AttrEsl, executor.esl as i32);
            executor_attr.set_u8_slice(AttributeKey::AttrPublicKey, &executor.public_key);

            sub_messages.push(executor_attr.to_bytes()?);
        }

        let sub_message_slices = sub_messages.iter().map(|v| v.as_slice()).collect::<Vec<&[u8]>>();
        let mut root_attr = Attribute::new();
        root_attr.set_u8_slices(AttributeKey::AttrAttrs, sub_message_slices.as_slice());

        let codec = MessageCodec::new(MessageSignParam::CrossDevice(input.remote_udid));
        let signed_data = codec.serialize_attribute(&root_attr)?;
        
        Ok(GetSignedExecutorInfoOutput {
            signed_executor_info: signed_data,
        })
    }
}

impl_command_handler!(
    GetSignedExecutorInfoCommandHandler,
    GetSignedExecutorInfoInputFfi,
    GetSignedExecutorInfoInput,
    GetSignedExecutorInfoOutputFfi,
    Box<dyn GetSignedExecutorInfoCommandExecutor>
);
